﻿/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "DBCStores.h"
#include <NodeCpp/String.h>
#include "Shared/DBC/DBCFileLoader.h"
#include "Shared/Common.h"
#include "Shared/ProgressBar.h"
#include "Shared/Log/Log.h"
#include "DBCFmt.h"
#ifdef _WIN32
#pragma warning(disable:4996)
#endif

typedef std::list<std::string> StoreProblemList;

static bool LoadDBC_assert_print(std::uint32_t _Fsize, std::uint32_t _Rsize, const std::string& _Filename)
{
	sLog.outError("Size of '%s' setted by format string (%u) not equal size of C++ structure (%u).", 
        _Filename.c_str(), _Fsize, _Rsize);
	return false;
}

void LoadDBC(std::uint32_t& _AvailableDbcLocales, BarGoLink& _Bar, StoreProblemList& _Errlist, 
    DBCStorage& _Storage, std::size_t _TypeSize, const std::string& _DbcPath, const std::string& _Filename)
{
	// 检查结构体大小和加载器记录大小是否一致
    NODECPP_ASSERT(DBCLoader::GetFormatRecordSize(_Storage.GetFormat()) == _TypeSize
        || LoadDBC_assert_print(DBCLoader::GetFormatRecordSize(_Storage.GetFormat()), _TypeSize, _Filename));

	std::string _DbcFilename = _DbcPath + _Filename;
    if (_Storage.Load<DBCFileLoader>(_DbcFilename.c_str()))
	{
		_Bar.Step();
		for (std::uint8_t _I = 0; G_FullLocaleNameList_[_I].Name; ++_I)
		{
			if (!(_AvailableDbcLocales & (1 << _I))) {
				continue;
			}

			std::string _DbcFilenameLoc = _DbcPath + G_FullLocaleNameList_[_I].Name + "/" + _Filename;
            if (!_Storage.LoadStringsFrom<DBCFileLoader>(_DbcFilenameLoc.c_str())) {
				_AvailableDbcLocales &= ~(1 << _I);
			}
		}
	}
	else
	{
		// DBC文件加载失败
		FILE* _F = fopen(_DbcFilename.c_str(), "rb");
		if (_F) {
			char _Buf[100];
			NodeCpp::snprintf(_Buf, 100, " (exist, but have %u fields instead %s) "
                "Wrong client version DBC file?", _Storage.GetFieldCount(), strlen(_Storage.GetFormat()));
			_Errlist.push_back(_DbcFilename + _Buf);
			fclose(_F);
		}
		else {
            // 没有找到这个DBC文件
			_Errlist.push_back(_DbcFilename);
		}
	}
}


DBCStores::DBCStores(void)
	: ChrRacesStore_(ChrRacesEntryfmt)
	, ChrClassesStore_(ChrClassesEntryfmt)
    , MapStore_(MapEntryfmt)
{

}

DBCStores::~DBCStores(void)
{

}

bool DBCStores::LoadDBCs(const std::string& _DataPath)
{
    /* DBC文件目录 */
	std::string _DbcPath = _DataPath + "dbc/";

    /* 要加载的DBC文件总数 */
	const std::uint32_t _DBCFilesCount = 3;

    /* 错误列表 */
	StoreProblemList _BadDbcFiles;

    // 加载文件
	{
		BarGoLink _Bar(_DBCFilesCount);
		// bitmask for index of fullLocaleNameList
		std::uint32_t _AvailableDbcLocales = 0xFFFFFFFF;

        LoadDBC(_AvailableDbcLocales, _Bar, _BadDbcFiles, ChrRacesStore_, sizeof(ChrRacesEntry), _DbcPath, "ChrRaces.dbc");
        LoadDBC(_AvailableDbcLocales, _Bar, _BadDbcFiles, ChrClassesStore_, sizeof(ChrClassesEntry), _DbcPath, "ChrClasses.dbc");
        LoadDBC(_AvailableDbcLocales, _Bar, _BadDbcFiles, MapStore_, sizeof(MapEntry), _DbcPath, "Map.dbc");
	}
	
    // 错误处理
    {
        if (_BadDbcFiles.size() >= _DBCFilesCount) {
            sLog.outError("\nIncorrect DataDir value in Game.conf or ALL required *.dbc files (%d) not found by path: %sdbc", _DBCFilesCount, _DataPath.c_str());
            return false;
        }
        else if (!_BadDbcFiles.empty())
        {
            std::string str;
            for (std::list<std::string>::iterator _Iter = _BadDbcFiles.begin();
                _Iter != _BadDbcFiles.end(); ++_Iter) {
                sLog.outError("%s", (*_Iter).c_str());
            }

            return false;
        }
    }

	sLog.outString(">> Initialized %d data stores\n", _DBCFilesCount);
	return true;
}

///////////////////////////////////////////////////////////////////////////////

const struct ChrRacesEntry* DBCStores::GetChrRace(std::uint32_t _ID) const 
{ return ChrRacesStore_.LookupEntry<struct ChrRacesEntry>(_ID); }
const struct ChrClassesEntry* DBCStores::GetChrClass(std::uint32_t _ID) const
{ return ChrClassesStore_.LookupEntry<struct ChrClassesEntry>(_ID); }
const struct MapEntry* DBCStores::GetMap(std::uint32_t _ID) const
{ return MapStore_.LookupEntry<struct MapEntry>(_ID); }